package org.filetransfer.file_transfer_java_backend.service;
import jakarta.annotation.Resource;
import jakarta.transaction.Transactional;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.filetransfer.file_transfer_java_backend.controller.FileController;
import org.filetransfer.file_transfer_java_backend.entity.File;
import org.filetransfer.file_transfer_java_backend.entity.FileUpload;
import org.filetransfer.file_transfer_java_backend.model.ServiceFileInfoModel;
import org.filetransfer.file_transfer_java_backend.repository.FileRepository;
import org.filetransfer.file_transfer_java_backend.repository.FileUploadRepository;
import org.filetransfer.file_transfer_java_backend.utils.FileName;
import org.filetransfer.file_transfer_java_backend.utils.MD5;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.file.Files;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@Slf4j
@Service
@Transactional
public class FileUploadService {
    @Value("${file.upload.dir}")
    @Getter
    private String ConfigFileStorageDir;
    @Resource
    private FileUploadRepository fileUploadRepository;
    @Resource
    private FileRepository fileRepository;
    private boolean verifyChunkMd5(String md5, byte[] data) {
        var realMd5 = MD5.calcBytes(data);
        var result = Objects.equals(md5, realMd5);
        if (!result) {
            log.error("Invalid MD5 checksum for chunk. Chunk: {} Real:{}", md5, realMd5);
        }
        return result;
    }
    // 获取上传中的文件(临时文件)存放路径
    private String getTempUploadingFilePath(String md5) {
        return ConfigFileStorageDir + "/temp/" + md5;
    }
    public String getUploadFilePath(String code) {
        return ConfigFileStorageDir + "/storage/" + code;
    }
    private void writeChunk(String md5, long chunkOffset, byte[] chunkData) {
        try (var fos = acquireUploadingFile(md5)) {
            // 定位
            fos.seek(chunkOffset);
            // 写入
            fos.write(chunkData);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    //    private void commitFile(String md5, String fileName) {
//        var tempFile = new java.io.File(getTempUploadingFilePath(md5));
//        var targetFile = new java.io.File(getUploadFilePath(md5, fileName));
//        if (targetFile.exists()) targetFile.delete();
//        if (!tempFile.renameTo(targetFile)) {
//            throw new RuntimeException("文件转移失败");
//        }
//        fileUploadRepository.deleteByMd5(md5);
//    }
    // 获取上传中的文件(临时文件)
    private RandomAccessFile acquireUploadingFile(String md5) throws IOException {
        var file = new java.io.File(getTempUploadingFilePath(md5));
        if (!file.exists()) {
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            file.createNewFile();
        }
        try {
            return new RandomAccessFile(file, "rw");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
//    private String singleUpload(NetworkFileChunk fileParam) {
//        byte[] filebyte = fileParam.getChunkData();
//        try (var fos = acquireFile(fileParam.getFileMd5())) {
//            fos.seek(0);
//            fos.write(filebyte);
//            File fileRecord = new File();
//            long size = filebyte.length;
//            long sizeMB = size / (1024 * 1024);
//            fileRecord.setFileName(fileParam.getFileName());
//            fileRecord.setFilePath(FILE_UPLOAD_DIR + "/" + fileParam.getFileMd5() + "/" + fileParam.getFileName());
//            fileRecord.setUploadTime(LocalDateTime.now());
//            fileRecord.setFileSize(sizeMB);
//            fileRecord.setMd5(fileParam.getFileMd5());
//            fileRecord.setDownloadCount(0);
//            fileRepository.save(fileRecord);
//        } catch (IOException e) {
//            return "上传文件失败: " + e.getMessage();
//        }
//        return "上传成功";
//    }
//    // 区块上传
//    // 同步
//    private synchronized void chunkUpload(NetworkFileChunk chunk) {
//        try (var fos = acquireFile(chunk.getFileMd5())) {
//            var offset = chunk.getChunkOffset();
//            var data = chunk.getChunkData();
//            // 定位
//            fos.seek(offset);
//            // 写入
//            fos.write(data, 0, data.length);
    /// /            FileChunkRecord fileChunkRecord = new FileChunkRecord();
    /// /            fileChunkRecord.setMd5(chunk.getFileMd5());
    /// /            fileChunkRecord.setChunk(chunk.getChunkIndex());
    /// /            fileChunkRecordRepository.save(fileChunkRecord);
//        } catch (IOException e) {
//            throw new RuntimeException(e);
//        }
//    }
//    public boolean complete(String md5) {
//        var file = new java.io.File(getTempUploadingFilePath(md5));
//        // 校验总文件的md5
//        var realMd5 = MD5.calcFile(file);
//        var result = realMd5.equals(md5);
//        if (!result) {
//            log.error("Invalid MD5 checksum for file. File: {} Real:{}", md5, realMd5);
//            return false;
//        }
//        //将文件从缓存转移
//        cleanUp(file, realMd5);
//        return true;
//    }
//    private void cleanUp(java.io.File file, String md5) {
//        if (file.exists()) {
//            file.delete();
//        }
//        fileUploadRepository.deleteByMd5(md5);
//    }
    // 是否存在这个md5的文件的上传记录
    public boolean exists(String md5) {
        return fileUploadRepository.existsByMd5(md5);
    }
    public record ChunkInfo(int chunkIndex, long chunkOffset, String chunkMD5) {}
    public ChunkInfo[] getAllChunks(String md5) {
        var fileInstanceOptional = fileUploadRepository.findByMd5(md5);
        if (fileInstanceOptional.isEmpty()) {// 未创建这个md5的记录
            throw new IllegalArgumentException("文件不存在");
        }
        var fileInstance = fileInstanceOptional.get();
        return fileInstance.getChunks().stream().map(chunk -> new ChunkInfo(chunk.getChunkIndex(), chunk.getChunkOffset(), chunk.getChunkMD5())).toArray(ChunkInfo[]::new);
    }
    // 开始上传一个文件
    public void newUpload(String md5, FileController.FileUploadInfo fileUploadInfo) {
        // 检查文件是否已经上传过
        var exists = fileUploadRepository.existsByMd5(md5);
        if (exists) {// 存在这个md5
            throw new IllegalArgumentException("文件已经上传过");
        }
        // 新建上传记录
        var newUpload = FileUpload.builder().fileName(fileUploadInfo.fileName()).md5(md5).fileSize(fileUploadInfo.totalSize()).build();
        fileUploadRepository.save(newUpload);
    }
    // 上传一个分片
    public void commitChunk(String md5, String chunkMd5, int chunkIndex, long chunkOffset, byte[] chunkData) {
        // 检查文件是否已经上传过
        var fileInstanceOptional = fileUploadRepository.findByMd5(md5);
        if (fileInstanceOptional.isEmpty()) {// 未创建这个md5的记录
            throw new IllegalArgumentException("文件不存在" + md5);
        }
        // 校验分片MD5
        if (!verifyChunkMd5(chunkMd5, chunkData)) {
            throw new IllegalArgumentException("分片MD5校验失败" + md5);
        }
        // 获取文件实例
        var fileInstance = fileInstanceOptional.get();
        // 将分片写入文件
        writeChunk(md5, chunkOffset, chunkData);
        // 写好文件后创建分片信息,给文件实例添加分片信息
        var chunk = FileUpload.FileChunk.builder().chunkIndex(chunkIndex).chunkOffset(chunkOffset).chunkMD5(md5).fileUpload(fileInstance).build();
        fileInstance.getChunks().add(chunk);
        // 保存文件实例
        fileUploadRepository.save(fileInstance);
    }
    public void complete(String md5) {
        // 检查文件是否已经上传过
        var fileInstanceOptional = fileUploadRepository.findByMd5(md5);
        if (fileInstanceOptional.isEmpty()) {// 未创建这个md5的记录
            throw new IllegalArgumentException("文件不存在");
        }
        // 获取文件实例
        var fileInstance = fileInstanceOptional.get();
        // 校验文件MD5
        var realMd5 = MD5.calcFile(new java.io.File(getTempUploadingFilePath(md5)));
        if (!realMd5.equals(md5)) {
            throw new IllegalArgumentException("完整文件MD5校验失败");
        }
        // 设置文件上传完成
        fileInstance.setCompleted(true);
        fileUploadRepository.save(fileInstance);
        // 将文件从缓存转移
//        commitFile(md5, fileInstance.getFileName());
        // 保存文件记录
//        var fileRecord = new org.filetransfer.file_transfer_java_backend.entity.File();
//        fileRecord.setFileName(fileInstance.getFileName());
//        fileRecord.setMd5(fileInstance.getMd5());
//        fileRecord.setFileSize(fileInstance.getFileSize());
//        fileRecord.setFilePath(FILE_UPLOAD_DIR + "/" + fileInstance.getMd5() + "/" + fileInstance.getFileName());
//        fileRecord.setDownloadCount(0);
//        fileRepository.save(fileRecord);
    }
    public List<File> moveFilesToStorage(Collection<String> files, String dir) {
        if (!fileUploadRepository.existsAllByMd5In(files)) {
            throw new IllegalArgumentException("有文件不存在");
        }
        var uploadList = fileUploadRepository.findAllByMd5In(files);
        if (uploadList.size() != files.size()) {
            throw new IllegalArgumentException("有文件未上传");
        }
        if (uploadList.stream().anyMatch(file -> !file.isCompleted())) {
            throw new IllegalArgumentException("有文件未上传完成");
        }
        //移除缓存记录
        fileUploadRepository.deleteAll(uploadList);
        return uploadList.stream().map(file -> {
            var tempFilePath = getTempUploadingFilePath(file.getMd5());
            var targetFilePath = dir + "/" + FileName.removeInvalidChars(file.getFileName());
            var targetFile = new java.io.File(targetFilePath);
            if (!targetFile.getParentFile().exists()) {
                targetFile.getParentFile().mkdirs();
            }
            // 将文件从缓存转移
            try {
                Files.move(java.nio.file.Paths.get(tempFilePath), java.nio.file.Paths.get(targetFilePath));
            } catch (IOException e) {
                log.error("文件转移失败", e);
                throw new RuntimeException(e);
            }
            fileUploadRepository.delete(file);
            return File.builder().fileName(file.getFileName()).uploadTime(file.getUpdateTime()).md5(file.getMd5()).fileSize(file.getFileSize()).filePath(targetFilePath).downloadCount(0).build();
        }).collect(Collectors.toList());
    }
//    public String check(String md5) {
//        File existingFile = fileRepository.findByMd5(md5);
//        if (existingFile != null) {
//            return "文件已经上传过";
//        }
//        List<FileChunkRecord> records = fileChunkRecordRepository.findByMd5(md5);
//        StringBuilder result = new StringBuilder();
//        for (FileChunkRecord record : records) {
//            result.append(record.getChunk());
//        }
//        return result.toString();
//    }
//
//
//    private boolean checkFileMd5(java.io.File file, String fileMd5) throws IOException {
//        FileInputStream fis = new FileInputStream(file);
//        String checkMd5 = DigestUtils.md5DigestAsHex(fis).toUpperCase();
//        fis.close();
//        return checkMd5.equals(fileMd5.toUpperCase());
//    }
//
//
//    private boolean renameFile(java.io.File toBeRenamed, String toFileNewName) {
//        if (!toBeRenamed.exists() || toBeRenamed.isDirectory()) {
//            return false;
//        }
//        String parentPath = toBeRenamed.getParent();
//        java.io.File newFile = new java.io.File(parentPath + java.io.File.separatorChar + toFileNewName);
//        if (newFile.exists()) {
//            newFile.delete();
//        }
//        return toBeRenamed.renameTo(newFile);
//    }
//
//    @Transactional
//    public void recordFile(String fileName, String fileMd5, String parentDir) {
//        java.io.File file = new java.io.File(parentDir + fileName);
//        long size = file.length();
//        long sizeMB = size / (1024 * 1024);
//        // 保存文件记录
//        File fileRecord = new File();
//        fileRecord.setFileName(fileName);
//        fileRecord.setMd5(fileMd5);
//        fileRecord.setFileSize(sizeMB);
//        fileRecord.setFilePath(file.getAbsolutePath());
//        fileRecord.setUploadTime(LocalDateTime.now());
//        fileRepository.save(fileRecord);
//        fileChunkRecordRepository.deleteByMd5(fileMd5);
//    }
}
